<?php

declare(strict_types=1);

namespace App\Job;

use App\Model\Product;
use App\Model\User;
use Hyperf\AsyncQueue\Job;
use Hyperf\DbConnection\Db;
use App\Service\NormalOrderQueueService;
use App\Service\NormalRollBackQueueService;
use Hyperf\Context\ApplicationContext;

class NormalBuyExampleJob extends Job
{
	public $params;

	// #[Inject]
	protected NormalOrderQueueService $normalOrder;
	protected NormalRollBackQueueService $normalRollBack;

	/**
	 * 任务执行失败后的重试次数，即最大执行次数为 $maxAttempts+1 次
	 */
	protected int $maxAttempts = 2;

	public function __construct($params)
	{
		$this->params = $params;
	}

	public function handle()
	{
		$container = ApplicationContext::getContainer();
		$normalOrder = $container->get(NormalOrderQueueService::class);
		$normalRollBack = $container->get(NormalRollBackQueueService::class);

		$shopData = json_decode($this->params[0], true);
		$orderId = $this->params[1];
		$userid = $this->params[2];

		Db::beginTransaction();
		try {
			$userInfo = User::query()->where('id', $userid)->lockForUpdate()->first();
			if (empty($userInfo)) {
				throw new \Exception('用户不存在');
			}

			$priceAll = 0;  //订单总价格
			$orderGoods = []; //订单的商品数组
			foreach ($shopData as $k => $v) {
				$productInfo = Product::query()->where('id', $v['product_id'])->lockForUpdate()->first();
				if (empty($productInfo)) {
					throw new \Exception('购买失败，商品id为' . $v['product_id'] . '，不存在了');
				}

				if ($productInfo->num < $v['num']) {
					throw new \Exception('购买失败，商品id为' . $v['product_id'] . '，数量不足');
				}

				//orderGood插入一条记录
				$orderGoods[] = [
					'user_id' => $userid,
					'order_id' => $orderId,
					'product_id' => $v['product_id'],
					'product_name' => $productInfo->name,
					'price' => $productInfo->price,
					'num' => $v['num'],
					'created_at' => date('Y-m-d H:i:s'),
					'updated_at' => date('Y-m-d H:i:s'),
				];

				//订单总价格叠加
				$priceAll += $productInfo->price * $v['num'];
			}

			if ($priceAll > $userInfo->balance) {
				throw new \Exception('购买失败，余额不足');
			}

			// 扣除库存
			foreach ($shopData as $k => $v) {
				$res = Product::query()->where('id', $v['product_id'])->where('num', '>=', $v['num'])->lockForUpdate()->decrement('num', $v['num']);
				if (!$res) {
					throw new \Exception('扣减库存失败');
				}
			}

			//扣余额
			$userResult = User::query()->where('id', $userid)->where('balance', '>=', $priceAll)->lockForUpdate()->decrement('balance', $priceAll);
			if (!$userResult) {
				throw new \Exception('扣除余额失败');
			}

			//订单处理
			$normalOrder->push([$orderGoods, $orderId, $userid, $priceAll]);

			Db::commit();
			return json_encode(['err' => 0, 'msg' => '购买成功']);
		} catch (\Throwable $e) {
			Db::rollBack();
			$normalRollBack->push($orderId);
			return json_encode(['err' => 1, 'msg' => $e->getMessage()]);
		}
	}

	public function handleRedis()
	{
		$container = ApplicationContext::getContainer();
		$normalOrder = $container->get(NormalOrderQueueService::class);
		$normalRollBack = $container->get(NormalRollBackQueueService::class);
		$redis = $container->get(\Hyperf\Redis\Redis::class);

		$shopData = json_decode($this->params[0], true);
		$oldNum = [];     //各商品的购买数量
		$orderId = $this->params[1];
		$userid = $this->params[2];

		Db::beginTransaction();
		try {
			$userInfo = User::query()->where('id', $userid)->lockForUpdate()->first();
			if (empty($userInfo)) {
				throw new \Exception('用户不存在');
			}

			$priceAll = 0;    //订单总价格
			$orderGoods = []; //订单的商品数组

			// 扣除库存
			foreach ($shopData as $k => $v) {
				$isExists = $redis->exists('product:normal:' . $v['product_id']);
				if (!$isExists) {
					throw new \Exception('购买失败，商品id为' . $v['product_id'] . '，不存在');
				}

				$productInfo = $redis->hgetall('product:normal:' . $v['product_id']);
				if (empty($productInfo)) {
					throw new \Exception('购买失败，商品id为' . $v['product_id'] . '，不存在了');
				}

				if ($productInfo['is_sale']) {
					throw new \Exception('购买失败，商品id为' . $v['product_id'] . '为抢购商品');
				}

				$v['num'] = intval($v['num']);
				if ($v['num'] <= 0) {
					throw new \Exception('购买失败，商品id为' . $v['product_id'] . '数量不大于0');
				}

				if ($productInfo['num'] < $v['num']) {
					throw new \Exception('购买失败，商品id为' . $v['product_id'] . '，数量不足');
				}

				// 监视商品，如果watch中的值被改变，事务就会终止并回滚
				$productKey = 'product:normal:' . $v['product_id'];
				$redis->watch($productKey);

				$redis->multi();

				//更新商品的剩余数量
				$redis->hIncrBy($productKey, 'num', -$v['num']);

				$result = $redis->exec();
				if (!$result) {
					throw new \Exception('减少库存失败');
				}

				//orderGood插入一条记录
				$orderGoods[] = [
					'user_id' => $userid,
					'order_id' => $orderId,
					'product_id' => $v['product_id'],
					'product_name' => $productInfo['name'],
					'price' => $productInfo['price'],
					'num' => $v['num'],
					'created_at' => date('Y-m-d H:i:s'),
					'updated_at' => date('Y-m-d H:i:s'),
				];

				//订单总价格叠加
				$priceAll += $productInfo['price'] * $v['num'];

				//把商品数量添加到该数组，用作回滚时还原商品的数量
				array_push($oldNum, $v['num']);
			}

			if ($priceAll > $userInfo->balance) {
				throw new \Exception('购买失败，余额不足');
			}

			// 扣余额
			$userResult = User::query()->where('id', $userid)->where('balance', '>=', $priceAll)->lockForUpdate()->decrement('balance', $priceAll);
			if (!$userResult) {
				throw new \Exception('扣除余额失败');
			}
			User::query()->where('id', $userid)->update(['updated_at' => date('Y-m-d H:i:s')]);

			// 订单处理
			$normalOrder->push([$orderGoods, $orderId, $userid, $priceAll]);

			Db::commit();
			return json_encode(['err' => 0, 'msg' => '购买成功']);
		} catch (\Throwable $e) {
			Db::rollBack();
			$this->rollbackProductQuantity($orderId, $shopData, $oldNum);
			return json_encode(['err' => 1, 'msg' => $e->getMessage()]);
		}
	}

	//商品回滚
	public function rollbackProductQuantity($orderId, $shopData, $oldNum)
	{
		//还原商品的数量
		$product = 'product:normal:';

		foreach ($oldNum as $k => $v) {
			$this->redis->hIncrBy($product . $shopData[$k]['product_id'], 'num', $v);
		}

		$this->normalRollBack->push($orderId);
	}
}
